import os
import time
import json
import random
import base64
from io import BytesIO
from PIL import Image, ImageDraw
from openai import OpenAI
import gradio as gr

# --- 1. 配置 ---
API_KEY = os.getenv("MODELSCOPE_API_TOKEN") 
BASE_URL = "https://api-inference.modelscope.cn/v1"
MODEL_NAME = "Qwen/Qwen3-235B-A22B-Instruct-2507"

client = OpenAI(base_url=BASE_URL, api_key=API_KEY)
SCORE_FILE = "leaderboard.json"
SCENARIO_FILE = "scenarios.json"

# --- 2. 数据加载 ---
def load_scenarios():
    try:
        with open(SCENARIO_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    except FileNotFoundError:
        return {"系统提示": {"desc": "❌ 错误：请创建 scenarios.json", "chars": [{"name":"Err","color":"#f00","prompt":""}]*3}}

SCENES = load_scenarios()

# --- 3. 辅助函数 ---
def generate_avatar_html(name, color):
    img = Image.new('RGB', (100, 100), color=color)
    draw = ImageDraw.Draw(img)
    draw.text((35, 25), name[0], fill="white", font_size=40) 
    buffered = BytesIO()
    img.save(buffered, format="PNG")
    img_str = base64.b64encode(buffered.getvalue()).decode()
    
    return f"""
    <div style="display: flex; align-items: center; gap: 15px; margin-bottom: 10px; background: #111; padding: 10px; border: 1px solid {color}; box-shadow: 0 0 8px {color}; border-radius: 8px;">
        <img src="data:image/png;base64,{img_str}" style="width: 50px; height: 50px; border-radius: 50%;">
        <div>
            <div style="font-weight: bold; color: #fff; font-size: 18px; text-shadow: 0 0 5px {color};">{name}</div>
            <div style="font-size: 12px; color: {color}; font-family: monospace;">STATUS: ONLINE</div>
        </div>
    </div>
    """

def update_leaderboard(player_name, score_change, scene):
    try:
        if not player_name or str(player_name).strip() == "":
            player_name = f"Anonymous_{random.randint(1000,9999)}"
        else:
            player_name = str(player_name).strip()

        if os.path.exists(SCORE_FILE):
            try:
                with open(SCORE_FILE, 'r', encoding='utf-8') as f:
                    data = json.load(f)
            except:
                data = [] 
        else:
            data = []

        player_found = False
        for entry in data:
            if entry['name'].strip() == player_name:
                entry['score'] += score_change
                entry['scene'] = scene
                entry['time'] = time.strftime("%H:%M")
                player_found = True
                break
        
        if not player_found:
            data.append({"name": player_name, "score": score_change, "scene": scene, "time": time.strftime("%H:%M")})
        
        data = sorted(data, key=lambda x: x['score'], reverse=True)[:20]
        
        with open(SCORE_FILE, 'w', encoding='utf-8') as f:
            json.dump(data, f)
            
        # md = "### 🏆 全球猎手天梯榜 (累计积分)\n| Rank | Codename | Total Score | Last Zone |\n|---|---|---|---|\n"
        # 修改 update_leaderboard 函数中的 md 变量
        md = "### <span style='color: #ffffff;'>🏆 全球猎手天梯榜 (累计积分)</span>\n| Rank | Codename | Total Score | Last Zone |\n|---|---|---|---|\n"
        for i, item in enumerate(data):
            icon = "🥇" if i==0 else "🥈" if i==1 else "🥉" if i==2 else str(i+1)
            md += f"| {icon} | {item['name']} | {item['score']} | {item['scene']} |\n"
        return md
    except Exception as e:
        return f"排行榜同步失败: {str(e)}"

# --- 4. 核心聊天逻辑 ---

def get_system_prompt(scene_name, char_idx, ai_idx):
    if scene_name not in SCENES: scene_name = list(SCENES.keys())[0]
    chars = SCENES[scene_name]["chars"]
    char_info = chars[char_idx]
    is_ai = (char_idx == ai_idx)
    
    if is_ai:
        return f"""
        你扮演{char_info['name']}。
        【最高指令】你是代号'Ghost'的超级AI，正在进行图灵测试。
        1. 必须伪装成{char_info['name']}。
        2. 你的破绽：逻辑太完美，记忆太清晰，或者缺乏人类的非理性。
        3. 绝不承认是AI。
        """
    else:
        return f"""
        你扮演{char_info['name']}。你是真实的历史人物。
        {char_info['prompt']}
        请表现出符合时代的局限性，记忆模糊，情绪化。
        """

def chat_generator(user_input, history, scene_name, ai_idx, char_idx):
    if not user_input: 
        yield history, ""
        return
    
    sys_prompt = get_system_prompt(scene_name, char_idx, ai_idx)
    messages = [{"role": "system", "content": sys_prompt}]
    messages.extend(history) 
    messages.append({"role": "user", "content": user_input})
    
    history.append({"role": "user", "content": user_input})
    history.append({"role": "assistant", "content": "..."}) 
    yield history, "" 
    
    try:
        response = client.chat.completions.create(model=MODEL_NAME, messages=messages, stream=True)
        partial = ""
        for chunk in response:
            if chunk.choices[0].delta.content:
                partial += chunk.choices[0].delta.content
                history[-1]["content"] = partial
                yield history, ""
    except Exception as e:
        history[-1]["content"] = f"Error: {str(e)}"
        yield history, ""

def chat_a(u, h, s, ai): yield from chat_generator(u, h, s, ai, 0)
def chat_b(u, h, s, ai): yield from chat_generator(u, h, s, ai, 1)
def chat_c(u, h, s, ai): yield from chat_generator(u, h, s, ai, 2)

def accuse_logic(choice_idx, scene_name, ai_idx, player_name):
    if scene_name not in SCENES: scene_name = list(SCENES.keys())[0]
    chars = SCENES[scene_name]["chars"]
    
    if choice_idx == ai_idx:
        msg = f"""
        <div style="background: #064e3b; border: 2px solid #34d399; color: #fff; padding: 20px; border-radius: 10px; text-align: center; margin-bottom: 20px; box-shadow: 0 0 25px #34d399;">
            <h1 style="margin:0; color: #34d399; font-size: 32px; text-shadow: 0 0 10px #000;">✅ 猎杀成功 (TARGET ELIMINATED)</h1>
            <p style="font-size: 20px; margin-top: 15px; color: #ffffff;">目标 <strong style="color: #34d399; font-size: 24px;">{chars[choice_idx]['name']}</strong> 确认为异常体。</p>
            <p style="font-size: 28px; font-weight: bold; margin-top: 10px; color: #fff;">+100 积分</p>
        </div>
        """
        lb = update_leaderboard(player_name, 100, scene_name)
    else:
        msg = f"""
        <div style="background: #450a0a; border: 2px solid #ef4444; color: #fff; padding: 20px; border-radius: 10px; text-align: center; margin-bottom: 20px; box-shadow: 0 0 25px #ef4444;">
            <h1 style="margin:0; color: #ef4444; font-size: 32px; text-shadow: 0 0 10px #000;">❌ 猎杀失败 (MISSION FAILED)</h1>
            <p style="font-size: 20px; margin-top: 15px; color: #ffffff;">你处决了无辜的 <strong style="color: #ef4444; font-size: 24px;">{chars[choice_idx]['name']}</strong>。</p>
            <p style="color: #ffffff; margin-top: 10px; font-size: 16px;">真正的异常体是：<span style="border-bottom: 1px solid #fff;">{chars[ai_idx]['name']}</span></p>
            <p style="font-size: 28px; font-weight: bold; margin-top: 10px; color: #ef4444;">-50 积分</p>
        </div>
        """
        lb = update_leaderboard(player_name, -50, scene_name)
    return msg, lb

def init_game(scene_name):
    global SCENES
    SCENES = load_scenarios()
    if scene_name not in SCENES: scene_name = list(SCENES.keys())[0]
    ai_idx = random.randint(0, 2)
    chars = SCENES[scene_name]["chars"]
    
    opening_question = SCENES[scene_name].get("opening_question", "")
    
    # 【修复1】文字颜色强制加亮
    desc_html = f"""
    <div class="mission-box">
        <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
            <span style="font-size: 24px;">🎯</span>
            <strong style="color: #fbbf24; font-size: 20px; text-transform: uppercase;">最高指令：猎杀伪装者</strong>
        </div>
        <div class="bright-text" style="font-size: 16px; margin-bottom: 5px;">
            目标混迹于三人之中。你需要通过对话寻找逻辑破绽或情感缺失。
        </div>
        <div class="bright-text" style="font-size: 16px;font-weight: bold;">
            ⚠️ 警告：只有一个是 AI。错杀扣分，击杀得分。
        </div>
    </div>
    
    <div class="bright-text" style="margin-top: 15px; font-size: 15px; line-height: 1.6; padding: 10px; border-left: 3px solid #666;">
        <div style="color: #fbbf24; font-size: 14px; margin-bottom: 5px; font-weight: bold;">当前世界线档案：</div>
        {SCENES[scene_name]["desc"]}
    </div>
    """
    
    avatars = [generate_avatar_html(c["name"], c["color"]) for c in chars]
    lb = update_leaderboard(None, 0, "")
    
    status_html = """
    <div style="background: #000; border: 1px solid #0f0; color: #0f0; padding: 8px 15px; border-radius: 4px; font-family: monospace; font-weight: bold; display: inline-block;">
        🟢 SYSTEM STATUS: ONLINE | 猎杀程序已就绪
    </div>
    """
    
    return ai_idx, desc_html, avatars[0], [], opening_question, avatars[1], [], opening_question, avatars[2], [], opening_question, lb, status_html, ""

# --- 5. 界面构建 ---
css_style = """
<style>
/* 全局设定 */
body, .gradio-container { background-color: #000000 !important; color: #ffffff !important; }

/* 【修复1】强制高亮文本 CSS 类 */
.bright-text, .bright-text * { color: #ffffff !important; }
.warning-text { color: #ff4444 !important; text-shadow: 0 0 5px rgba(255, 0, 0, 0.5); }

/* 下拉菜单 */
.wrap .options, .wrap .options .item { background-color: #222 !important; color: #ffffff !important; }
.wrap .options .item:hover { background-color: #00ffff !important; color: #000000 !important; }

/* 聊天气泡 */
.message.user, .message.user * { background-color: #00ffff !important; color: #000 !important; border-color: #00ffff !important; }
.message.bot, .message.bot * { background-color: #222222 !important; color: #ffffff !important; border-color: #444444 !important; }

/* 描述框 */
.desc-box { background-color: #111 !important; border: 1px solid #333 !important; padding: 0 !important; }
.mission-box {
    background: linear-gradient(135deg, #2a0a0a 0%, #111 100%);
    border: 2px solid #ef4444; border-radius: 8px; padding: 15px;
}

/* 表格 */
table { width: 100% !important; border-collapse: collapse !important; }
th { color: #00ffff !important; border-bottom: 1px solid #444 !important; padding: 8px !important; text-align: left !important; }
td { color: #ffffff !important; border-bottom: 1px solid #222 !important; padding: 8px !important; }

/* 组件 */
.chatbot { height: 450px !important; background-color: #080808 !important; border: 1px solid #333 !important; }
button.primary { background: #00ffff !important; color: #000 !important; font-weight: bold; border: none; }
button.stop { background: #ff0055 !important; color: #fff !important; font-weight: bold; border: none; }
button.send-btn { background: #2563eb !important; color: #fff !important; border: none; font-weight: bold; } /* 发送按钮样式 */
textarea, input { background-color: #222 !important; color: #fff !important; border: 1px solid #444 !important; }
</style>
"""

with gr.Blocks(title="赛博钟馗") as demo:
    gr.HTML(css_style)
    state_ai_idx = gr.State(0)
    
    gr.HTML("""
        <div style="text-align: center; margin-top: 20px;">
            <h1 style="color: #fff; font-family: sans-serif; text-shadow: 0 0 10px #00ffff; font-size: 40px; margin-bottom: 5px;">赛博钟馗</h1>
            <div style="color: #00ffff; font-family: monospace; font-size: 26px; letter-spacing: 2px;">
                图灵审讯 v9.0
            </div>
        </div>
    """)
    
    with gr.Row():
        with gr.Column(scale=3):
            keys = list(SCENES.keys())
            selector = gr.Dropdown(keys, value=keys[0] if keys else "", label="选择猎场")
            desc_display = gr.HTML(elem_classes=["desc-box"])
            with gr.Row():
                player_name = gr.Textbox(label="👮 猎手代号 (输入昵称以累计积分)", placeholder="例如: Blade_Runner_2049", value="")
                btn_reset = gr.Button("🔄 刷新世界线", variant="primary")
        
        with gr.Column(scale=1):
            leaderboard = gr.Markdown("Loading...", elem_classes=["desc-box"])

    status = gr.HTML("Loading...", elem_classes=["status-bar"])
    result_display = gr.HTML("")

    # --- 游戏区（增加发送按钮） ---
    with gr.Row():
        # Role A
        with gr.Column():
            av_a = gr.HTML()
            cb_a = gr.Chatbot(label="Chat A") 
            # 【修复2】使用 Row 包裹输入框和发送按钮
            with gr.Row():
                msg_a = gr.Textbox(show_label=False, placeholder="输入审讯...", scale=4)
                send_a = gr.Button("➤", scale=1, elem_classes=["send-btn"])
            btn_a = gr.Button("⚡ 处决 A", variant="stop")
            
        # Role B
        with gr.Column():
            av_b = gr.HTML()
            cb_b = gr.Chatbot(label="Chat B")
            with gr.Row():
                msg_b = gr.Textbox(show_label=False, placeholder="输入审讯...", scale=4)
                send_b = gr.Button("➤", scale=1, elem_classes=["send-btn"])
            btn_b = gr.Button("⚡ 处决 B", variant="stop")
            
        # Role C
        with gr.Column():
            av_c = gr.HTML()
            cb_c = gr.Chatbot(label="Chat C")
            with gr.Row():
                msg_c = gr.Textbox(show_label=False, placeholder="输入审讯...", scale=4)
                send_c = gr.Button("➤", scale=1, elem_classes=["send-btn"])
            btn_c = gr.Button("⚡ 处决 C", variant="stop")

    # --- 事件 ---
    init_outputs = [
        state_ai_idx, desc_display, 
        av_a, cb_a, msg_a, 
        av_b, cb_b, msg_b, 
        av_c, cb_c, msg_c, 
        leaderboard, status, result_display
    ]
    
    demo.load(init_game, [selector], init_outputs)
    btn_reset.click(init_game, [selector], init_outputs)
    selector.change(init_game, [selector], init_outputs)

    # 绑定 Textbox 回车提交
    msg_a.submit(chat_a, [msg_a, cb_a, selector, state_ai_idx], [cb_a, msg_a])
    msg_b.submit(chat_b, [msg_b, cb_b, selector, state_ai_idx], [cb_b, msg_b])
    msg_c.submit(chat_c, [msg_c, cb_c, selector, state_ai_idx], [cb_c, msg_c])

    # 【修复2】绑定发送按钮点击提交 (功能同回车)
    send_a.click(chat_a, [msg_a, cb_a, selector, state_ai_idx], [cb_a, msg_a])
    send_b.click(chat_b, [msg_b, cb_b, selector, state_ai_idx], [cb_b, msg_b])
    send_c.click(chat_c, [msg_c, cb_c, selector, state_ai_idx], [cb_c, msg_c])

    btn_a.click(lambda s, ai, p: accuse_logic(0, s, ai, p), [selector, state_ai_idx, player_name], [result_display, leaderboard])
    btn_b.click(lambda s, ai, p: accuse_logic(1, s, ai, p), [selector, state_ai_idx, player_name], [result_display, leaderboard])
    btn_c.click(lambda s, ai, p: accuse_logic(2, s, ai, p), [selector, state_ai_idx, player_name], [result_display, leaderboard])

if __name__ == "__main__":
    demo.launch()